View Javadoc

1   /*
2    * Copyright (C) 1998-2001 Semiotek Inc.  All Rights Reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted under the terms of either of the following
6    * Open Source licenses:
7    *
8    * The GNU General Public License, version 2, or any later version, as
9    * published by the Free Software Foundation
10   * (http://www.fsf.org/copyleft/gpl.html);
11   *
12   *  or
13   *
14   * The Semiotek Public License (http://webmacro.org/LICENSE.)
15   *
16   * This software is provided "as is", with NO WARRANTY, not even the
17   * implied warranties of fitness to purpose, or merchantability. You
18   * assume all risks and liabilities associated with its use.
19   *
20   * See www.webmacro.org for more information on the WebMacro project.
21   */
22  
23  package org.webmacro.directive;
24  
25  /***
26   * This directive allows the instantiation of objects in WMScript.
27   */
28  
29  import org.webmacro.*;
30  import org.webmacro.engine.*;
31  import org.webmacro.util.Instantiator;
32  
33  import java.io.IOException;
34  import java.util.HashMap;
35  import java.util.Map;
36  /***
37   * Implements a directive allowing a script writer
38   * to instantiate an arbitrary
39   * java object in a WebMacro template subject to 
40   * security restrictions.
41   */
42  
43  public class BeanDirective extends Directive
44  {
45  
46     private static final String APP_BEANS_KEY = "org.webmacro.directive.BeanDirective.appBeans";
47  
48     private static final int BEAN_TARGET = 1;
49  
50     private static final int BEAN_CLASS_NAME = 2;
51  
52     private static final int BEAN_SCOPE = 3;
53  
54     private static final int BEAN_SCOPE_GLOBAL = 4;
55  
56     private static final int BEAN_SCOPE_APPLICATION = 5;
57  
58     private static final int BEAN_SCOPE_SESSION = 6;
59  
60     private static final int BEAN_SCOPE_PAGE = 7;
61  
62     private static final int BEAN_INITARGS = 8;
63  
64     private static final int BEAN_INITARGS_VAL = 9;
65  
66     private static final int BEAN_TYPE_STATIC = 10;
67  
68     private static final int BEAN_ON_NEW = 11;
69  
70     private static final int BEAN_ON_NEW_BLOCK = 12;
71  
72     private static final UndefinedMacro UNDEF = UndefinedMacro.getInstance();
73  
74     private static int[] BEAN_SCOPES =
75     { BEAN_SCOPE_GLOBAL, BEAN_SCOPE_APPLICATION, BEAN_SCOPE_SESSION,
76              BEAN_SCOPE_PAGE };
77  
78     static Map globalBeans = new HashMap(20);
79  
80     private Variable target;
81  
82     private String targetName;
83  
84     private String _className;
85  
86     private int scope;
87  
88     private boolean isStaticClass;
89  
90     private Object initArgObj;
91  
92     private Object[] initArgs;
93  
94     private Block onNewBlock;
95  
96     private Log _log;
97  
98     private Broker _broker;
99  
100    private static final ArgDescriptor[] myArgs = new ArgDescriptor[]
101    { new LValueArg(BEAN_TARGET), new AssignmentArg(),
102             new QuotedStringArg(BEAN_CLASS_NAME), new OptionalGroup(3),
103             new KeywordArg(BEAN_SCOPE, "scope"), new AssignmentArg(),
104             new SingleOptionChoice(5), new OptionalGroup(1),
105             new KeywordArg(BEAN_SCOPE_GLOBAL, "global"), new OptionalGroup(1),
106             new KeywordArg(BEAN_SCOPE_APPLICATION, "application"),
107             new OptionalGroup(1),
108             new KeywordArg(BEAN_SCOPE_SESSION, "session"),
109             new OptionalGroup(1), new KeywordArg(BEAN_SCOPE_PAGE, "page"),
110             new OptionalGroup(1), new KeywordArg(BEAN_TYPE_STATIC, "static"),
111             new OptionalGroup(2), new KeywordArg(BEAN_INITARGS, "initArgs"),
112             new RValueArg(BEAN_INITARGS_VAL), new OptionalGroup(2),
113             new KeywordArg(BEAN_ON_NEW, "onNew"),
114             new BlockArg(BEAN_ON_NEW_BLOCK) };
115 
116    private static final DirectiveDescriptor myDescr = new DirectiveDescriptor(
117             "bean", null, myArgs, null);
118 
119    public static DirectiveDescriptor getDescriptor()
120    {
121       return myDescr;
122    }
123 
124    public BeanDirective()
125    {
126    }
127 
128    public Object build(DirectiveBuilder builder, BuildContext bc)
129             throws BuildException
130    {
131       _broker = bc.getBroker();
132       _log = _broker.getLog("directive");
133       // appBeans map is created by the init method when this directive
134       // is registered by the DirectiveProvider
135       try
136       {
137          target = (Variable) builder.getArg(BEAN_TARGET, bc);
138       }
139       catch (ClassCastException e)
140       {
141          throw new NotVariableBuildException(myDescr.name, e);
142       }
143       targetName = target.getName();
144       _className = (String) builder.getArg(BEAN_CLASS_NAME, bc);
145       classForName(_className);
146 
147       // check if bean is declared as static
148       // this implies global scope and no constructor invocation
149       isStaticClass = (builder.getArg(BEAN_TYPE_STATIC) != null);
150       if (isStaticClass)
151          scope = BEAN_SCOPE_GLOBAL;
152       else
153       {
154          scope = getScope(builder, bc);
155          // initArgs is only valid for non-static beans
156          initArgObj = builder.getArg(BEAN_INITARGS_VAL);
157          if (initArgObj instanceof Builder)
158             initArgObj = ((Builder) initArgObj).build(bc);
159       }
160       onNewBlock = (Block) builder.getArg(BEAN_ON_NEW_BLOCK, bc);
161 
162       _log.debug("BeanDirective, target=" + target + ", className="
163                + _className + ", scope=" + scope + ", isStaticClass="
164                + isStaticClass + ", initArgs=" + initArgObj);
165       return this;
166    }
167 
168    public void write(FastWriter out, Context context) throws PropertyException,
169             IOException
170    {
171       Map appBeans = (Map) _broker.getBrokerLocal(APP_BEANS_KEY);
172 
173       // = beanConf.appBeans;
174       boolean isNew = false;
175 
176       try
177       {
178          while (initArgObj instanceof Macro && initArgObj != UNDEF)
179             initArgObj = ((Macro) initArgObj).evaluate(context);
180 
181          // store init args in array
182          if (initArgObj == null || initArgObj.getClass().isArray())
183          {
184             initArgs = (Object[]) initArgObj;
185          }
186          else
187          {
188             initArgs = new Object[]
189             { initArgObj };
190          }
191 
192          Object o = null;
193          Class c = null;
194          switch (scope)
195          {
196             case BEAN_SCOPE_GLOBAL:
197                synchronized (globalBeans)
198                {
199                   o = globalBeans.get(targetName);
200                   if (o == null)
201                   {
202                      if (isStaticClass)
203                      {
204                         c = context.getBroker().classForName(_className);
205                         o = new org.webmacro.engine.StaticClassWrapper(c);
206                      }
207                      else
208                      {
209                         // c = Class.forName(_className);
210                         // o = c.newInstance();
211                         o = instantiate(_className, initArgs);
212                      }
213                      isNew = true;
214                      globalBeans.put(targetName, o);
215                   }
216                }
217                break;
218 
219             case BEAN_SCOPE_APPLICATION:
220                synchronized (appBeans)
221                {
222                   o = appBeans.get(targetName);
223                   if (o == null)
224                   {
225                      o = instantiate(_className, initArgs);
226                      isNew = true;
227                      appBeans.put(targetName, o);
228                   }
229                }
230                break;
231 
232             case BEAN_SCOPE_SESSION:
233                javax.servlet.http.HttpSession session = (javax.servlet.http.HttpSession) context
234                         .getProperty("Session");
235                // if (context instanceof WebContext){
236                if (session != null)
237                {
238                   synchronized (session)
239                   {
240                      o = session.getAttribute(targetName);
241                      if (o == null)
242                      {
243                         o = instantiate(_className, initArgs);
244                         isNew = true;
245                         session.setAttribute(targetName, o);
246                      }
247                   }
248                }
249                else
250                {
251                   PropertyException e = new PropertyException(
252                            "#bean usage error: session scope is only valid with servlets!");
253                   _broker.getEvaluationExceptionHandler().evaluate(target,
254                            context, e);
255                }
256                break;
257             default:
258                // make "page" the default scope
259                // case BEAN_SCOPE_PAGE:
260                // NOTE: page beans always overwrite anything in the context
261                // with the same name
262                o = instantiate(_className, initArgs);
263                isNew = true;
264                if (o != null)
265                {
266                   Class[] paramTypes =
267                   { Context.class };
268                   try
269                   {
270                      java.lang.reflect.Method m = o.getClass().getMethod(
271                               "init", paramTypes);
272                      if (m != null)
273                      {
274                         Object[] args =
275                         { context };
276                         m.invoke(o, args);
277                      }
278                   }
279                   catch (Exception e)
280                   { // ignore
281                   }
282                }
283                break;
284          }
285 
286          _log.debug("BeanDirective: Class " + _className + " loaded.");
287          target.setValue(context, o);
288       }
289       catch (PropertyException e)
290       {
291          this._broker.getEvaluationExceptionHandler().evaluate(target, context,
292                   e);
293       }
294       catch (Exception e)
295       {
296          String errorText = "BeanDirective: Unable to load bean " + target
297                   + " of type " + _className;
298          writeWarning(errorText, context, out);
299       }
300       if (isNew && onNewBlock != null)
301          onNewBlock.write(out, context);
302 
303    }
304 
305    public void accept(TemplateVisitor v)
306    {
307       v.beginDirective(myDescr.name);
308       v.visitDirectiveArg("BeanTarget", target);
309       v.visitDirectiveArg("BeanClass", _className);
310       v.visitDirectiveArg("BeanScope", new Integer(scope));
311       v.visitDirectiveArg("BeanIsStatic", new Boolean(isStaticClass));
312       v.visitDirectiveArg("BeanInitArgs", initArgs);
313       v.endDirective();
314    }
315 
316    private Object instantiate(String className, Object[] args) throws Exception
317    {
318       Class c = classForName(className);
319       return instantiate(c, args);
320    }
321 
322    private Object instantiate(Class c, Object[] args) throws Exception
323    {
324       return Instantiator.getInstance(_broker).instantiate(c, args);
325    }
326 
327    private static int getScope(DirectiveBuilder builder, BuildContext bc)
328             throws org.webmacro.engine.BuildException
329    {
330       int scope = -1;
331 
332       for (int i = 0; i < BEAN_SCOPES.length; i++)
333       {
334          scope = BEAN_SCOPES[i];
335          if (builder.getArg(scope) != null)
336             break;
337       }
338       return scope;
339    }
340 
341    public static void init(Broker b)
342    {
343       // get configuration parameters
344       synchronized (b)
345       {
346          b.setBrokerLocal(APP_BEANS_KEY, new HashMap(20));
347       }
348    }
349 
350    private Class classForName(String className) throws BuildException
351    {
352 
353       Class c;
354       try
355       {
356          c = Instantiator.getInstance(_broker).classForName(className);
357       }
358       catch (WebMacroException e)
359       {
360          throw new BuildException("BeanDirective failed to load class \""
361                   + className + "\"", e);
362       }
363       return c;
364    }
365 }